/*
 * Copyright (c) 2011 Joe Kopena <tjkopena@gmail.com>
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use, copy,
 * modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.

 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
*/

package com.rocketshipgames.haxe;

import flash.media.Sound;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Loader;
import flash.net.URLRequest;

import flash.events.Event;
import flash.events.IOErrorEvent;

class AssetCache {

  // Originally AssetCache was not a singleton, but it was an
  // unnecessary hassle shipping around the cache object all over the
  // place.  For the envisioned types of projects, this usage pattern
  // will be understandable and totally acceptable.

  private static var _instance:AssetCache = null;

  private var _notifier:Void->Void;

  private var _bitmapDataCache:Hash<BitmapData>;
  private var _soundCache:Hash<Sound>;

  private var _count:Int;
  private var _lock:Bool;

  public var errorMsg:String;
  public var error:Bool;

  //--------------------------------------------------------------------
  //--------------------------------------------------------------------
  private function new():Void
  {
    _bitmapDataCache = new Hash<BitmapData>();
    _soundCache = new Hash<Sound>();
    _count = 0;
    _lock = false;
    error = false;
    _notifier = null;
  }

  inline static public function instance():AssetCache
  {
    return (_instance == null) ? (_instance = new AssetCache()) : _instance;
  }

  public function lock(notifier:Void->Void):Void
  {
    trace("Locked");
    _lock = true;
    _notifier = notifier;
    completenessCheck();
  }


  private function completenessCheck():Void
  {
    if (_lock && _count <= 0) {
      trace("Completed loading all assets" +
	    ((error)?"; there were errors":""));
      if (_notifier != null)
	_notifier();
    }
    // end function completenessCheck
  }

  //--------------------------------------------------------------------
  //--------------------------------------------------------------------
  public function addBitmapData(path:String, ?bmp:Bitmap=null,
				?embedded:Bool=false)
  {
    if (_lock)
      trace("Unlocking locked cache");
    _lock = false;
    _count++;

    if (bmp == null) {
      trace("Loading resource '" + path + "' [" +
	    ((embedded)?"embedded":"external") + "]");
      var loader:Loader = new Loader();
      loader.contentLoaderInfo.addEventListener
	(Event.COMPLETE,
	 function(_) {
	  trace("Done loading '" + path + "'");
	  _bitmapDataCache.set(path, untyped loader.content.bitmapData);
	  _count--;
	  completenessCheck();
	});
      loader.contentLoaderInfo.addEventListener
	(IOErrorEvent.IO_ERROR,
	 function(e:IOErrorEvent) {
	  newError("Error loading '" + path + "':\n" + e.text);
	  _count--;
	  completenessCheck();
	});

      if (embedded) {
	var bytes:haxe.io.Bytes = haxe.Resource.getBytes(path);
	if (bytes != null)
	  loader.loadBytes(bytes.getData());
	else {
	  newError("Could not load bytes for embedded resource '" + path + "'");
	  _count--;
	}
      } else {
	loader.load(new URLRequest(path));
      }
      // end was not given resource class
    } else {
      trace("Given bitmap '" + path);
      _bitmapDataCache.set(path, bmp.bitmapData);
      _count--;
      completenessCheck();
    }

    // end function addBitmapData
  }

  public function getBitmapData(path:String):BitmapData {
    return _bitmapDataCache.get(path);
  }

  //--------------------------------------------------------------------
  //--------------------------------------------------------------------
  public function addSound(path:String, ?snd:Sound=null)
  {
    if (_lock)
      trace("Unlocking locked cache");
    _lock = false;
    _count++;

    if (snd == null) {
      trace("Loading '" + path + "' [external]");
      var sound:Sound = new Sound();
      sound.addEventListener
	(Event.COMPLETE,
	 function(_) {
	  trace("Done loading sound '" + path + "'");
	  _soundCache.set(path, sound);
	  _count--;
	  completenessCheck();
	});
      sound.addEventListener
	(IOErrorEvent.IO_ERROR,
	 function(e:IOErrorEvent) {
	  newError("Error loading sound '" + path + "':\n" + e.text);
	  _count--;
	  completenessCheck();
	});

      sound.load(new URLRequest(path));
    } else {
	trace("Given sound '" + path + "'");
	_soundCache.set(path, snd);
	_count--;
	completenessCheck();
    }

    // end function addSound
  }

  public function getSound(path:String):Sound {
    return _soundCache.get(path);
  }

  //--------------------------------------------------------------------
  //--------------------------------------------------------------------
  private function newError(msg:String):Void
  {
    trace(msg);
    if (errorMsg == null)
      errorMsg += "\n";
    errorMsg += msg;
    error = true;
    // end function newError
  }

  // end class AssetCache
}
